/**
 * Created by cher on 2016/6/21.
 * 本地数据库操作
 */
;(function(factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else {
        window.driver = factory();
    }
})(function() {
    var window = this
    "use strict";
    Array.prototype.contains = function (elem) {
        var s =  String.fromCharCode(2);
        var r = new RegExp(s + elem + s);
        return (r.test(s + this.join(s) + s));
    }

    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function (elem) {
            for (var i = 0; i < this.length; i++) {
                if (i in this && this[i] === elem) {
                    return i;
                }
            }
            return -1;
        }
    }
    Date.parseDate = function(dateStr){
        var date = (0,eval)('new Date(' + dateStr.replace(/\d+(?=-[^-]+$)/,
                function (a) { return parseInt(a, 10) - 1; }).match(/\d+/g) + ')');
        date.setMonth(date.getMonth());
        return date;
    }
    Date.format = function(date, pattern){
        pattern = pattern || "yyyy-MM-dd HH:mm:ss SSS";
        return (date || new Date()).format(pattern);
    }
    Date.prototype.format=function(fmt) {
        var o = {
            "M+" : this.getMonth()+1, //月份
            "d+" : this.getDate(), //日
            "h+" : this.getHours()%12 == 0 ? 12 : this.getHours()%12, //小时
            "H+" : this.getHours(), //小时
            "m+" : this.getMinutes(), //分
            "s+" : this.getSeconds(), //秒
            "q+" : Math.floor((this.getMonth()+3)/3), //季度
            "S" : this.getMilliseconds() //毫秒
        };
        var week = {
            "0" : "/u65e5",
            "1" : "/u4e00",
            "2" : "/u4e8c",
            "3" : "/u4e09",
            "4" : "/u56db",
            "5" : "/u4e94",
            "6" : "/u516d"
        };
        if(/(y+)/.test(fmt)){
            fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
        }
        if(/(E+)/.test(fmt)){
            fmt=fmt.replace(RegExp.$1, ((RegExp.$1.length>1) ? (RegExp.$1.length>2 ? "/u661f/u671f" : "/u5468") : "")+week[this.getDay()+""]);
        }
        for(var k in o){
            if(new RegExp("("+ k +")").test(fmt)){
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
            }
        }
        return fmt;
    }
    var utils = {
        isFunction: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Function]';
        },
        isUndefined: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Undefined]';
        },
        isNotUndefined: function (obj) {
            return !this.isUndefined(obj);
        },
        isNull: function (obj) {
            return obj == null;
        },
        isNotNull: function (obj) {
            return !this.isNull(obj);
        },
        isBlank: function (obj) {
            return this.isNull(obj) || this.isUndefined(obj) || obj == "";
        },
        isEmpty: function (obj) {
            if (this.isObject(obj)) {
                for (var p in obj) {
                    if (this.isFunction(obj[p])) continue;
                    return true;
                }
            } else if (this.isArray(obj) || this.isString(obj)) {
                return obj.length == 0;
            }
            return false;
        },
        isArray: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Array]';
        },
        isString: function (obj) {
            return Object.prototype.toString.call(obj) === '[object String]';
        },
        isObject: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Object]';
        },
        isArguments: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Arguments]';
        },
        nowMilliseconds: function () {
            return new Date().getTime();
        },
        each: function (obj, fn) {
            obj = obj || {}
            fn = fn || utils.noop
            if(obj.forEach) {
                obj.forEach(function(item, index, array){
                    if (fn.call(item, index, item, array) === false) return
                })
            } else {
                for (var p in obj) {
                    if (fn.call(obj[p], p, obj[p], obj) === false) break
                }
            }
        },
        recursion: function (obj, fn) {
            utils.each(obj, function (key, val, target) {
                if(utils.isFunction(val)) return
                fn.call(val, key, val, target)
                if(utils.isObject(val) || utils.isArray(val)){
                    utils.recursion(val, fn)
                }
            })
        },
        extend: function () {
            var rs = arguments[0] = arguments[0] || {}
            if (arguments.length <= 1) {
                return rs
            } else {
                if (!utils.isObject(arguments[1]) && !utils.isArray(arguments[1])) {
                    rs = arguments[1]
                } else {
                    utils.each(arguments[1], function (k, v) {
                        if(v === null || undefined === v) return
                        utils.isUndefined(rs[k]) && (rs[k] = utils.isArray(arguments[1]) ? [] :{})
                        rs[k] = utils.extend(rs[k], v)
                    })
                }
                for (var i = 2; i < arguments.length; i++) {
                    utils.extend(rs, arguments[i])
                }
            }
            return rs
        },
        noop: function () {
        }
    }

    /**
     * 事务的模式
     * READ：读
     * READ_WRITE：读写
     * VERION_CHANGE: 版本变更
     * @type {{READ: string, READ_WRITE: string, VERION_CHANGE: string}}
     */
    var TransactionMode = {READONLY:"readonly", READ_WRITE:"readwrite", READ_WRITE_FLUSH:"readwriteflush ", VERION_CHANGE:"verionchange"};
    /*
     * 游标循环的方向
     * CursorDirection.NEXT                     顺序循环
     * CursorDirection.NEXT_NO_DUPLICATE        顺序循环不重复.当取值有重复时，只获取一次。
     * CursorDirection.PREV                     倒序循环
     * CursorDirection.PREV_NO_DUPLICATE        倒序循环不重复.当取值有重复时，只获取一次。
     * @type {{NEXT: *, NEXT_NO_DUPLICATE: *, PREV: *, PREV_NO_DUPLICATE: *}}
     */
    window.CursorDirection = {NEXT:IDBCursor.NEXT, NEXT_NO_DUPLICATE:IDBCursor.NEXT_NO_DUPLICATE, PREV:IDBCursor.PREV, PREV_NO_DUPLICATE:IDBCursor.PREV_NO_DUPLICATE};


    /**
     *  IDBKeyRange All keys ≤ x 	IDBKeyRange.upperBound(x)
     *  All keys < x 	IDBKeyRange.upperBound(x, true)
     *  All keys ≥ y 	IDBKeyRange.lowerBound(y)
     *  All keys > y 	IDBKeyRange.lowerBound(y, true)
     *  All keys ≥ x && ≤ y 	IDBKeyRange.bound(x, y)
     *  All keys > x &&< y 	IDBKeyRange.bound(x, y, true, true)
     *  All keys > x && ≤ y 	IDBKeyRange.bound(x, y, true, false)
     *  All keys ≥ x &&< y 	IDBKeyRange.bound(x, y, false, true)
     *  The key = z 	IDBKeyRange.only(z)
     * @type
     * {{
     *      ltEq: Window.KeyRange.ltEq,
     *      lt: Window.KeyRange.lt,
     *      gtEq: Window.KeyRange.gtEq,
     *      gt: Window.KeyRange.gt,
     *      between: Window.KeyRange.between,
     *      openInterval: Window.KeyRange.openInterval,
     *      gtLtEq: Window.KeyRange.gtLtEq,
     *      gtEqLt: Window.KeyRange.gtEqLt,
     *      eq: Window.KeyRange.eq
     *  }}
     */
    window.KeyRange = {
        ltEq : function(x){return DBKeyRange.upperBound(x);},
        lt : function(x){return DBKeyRange.upperBound(x, true);},
        gtEq : function(x){return DBKeyRange.lowerBound(x);},
        gt : function(x){return DBKeyRange.lowerBound(x, true);},
        between : function(x, y){return DBKeyRange.bound(x, y);},
        gtLt : function(x, y){return DBKeyRange.bound(x, y, true, true);},
        gtLtEq : function(x, y){return DBKeyRange.bound(x, y, true, false);},
        gtEqLt : function(x, y){return DBKeyRange.bound(x, y, false, false);},
        eq : function(x){return DBKeyRange.only(x);},
        ALL_KEYS:"_ALL_KEYS"
    };
    window.innerDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    window.DBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
    window.DBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
    window.INDEXDB_STATUES = {
        CONNECT: {ING: 0, SUCCESS: 1, ERROR: 2,  CHANGE: 3, CLOSE: 4},
        DATABASE: {ERROR: 0, ABORT: 1}
    }
    /*****************************************Database********************************************/
    /**
     * 数据库对象
     */
    function Database(name, version){
        this.name = name || ''
        this.version = version || 1
        this.listener = utils.noop
        this.connect = null
    }
    Database.prototype.addListener = function (listener) {
        this.listener = utils.isFunction(listener) ? listener : utils.noop
    }
    /**
     * 打开数据库
     * @param options
     * @param version
     */
    Database.prototype.open = function (options, version) {
        var opts = {
            name: '',
            version: version || 1,
            tables: [],
            success:function(){},//数据库打开链接成功
            callback:function(){},//z最后的回调函数
            close:function(){},//数据库关闭
            change:function(){},//版本改变
            error:function(){},//数据库打开链接错误或者数据库错误
            abort:function(){}//数据库中断
        }
        var self = this
        options = utils.extend(opts, utils.isString(options) ? {name:options}:options)
        var dbRequest = innerDB.open(options.name || self.name, options.version)
        self.listener(INDEXDB_STATUES.CONNECT.ING)
        dbRequest.onerror = function(e){
            var error = e.target.error
            console.error(error.message)
            self.listener(INDEXDB_STATUES.CONNECT.ERROR)
            options.error.call(self,error)
            options.callback.call(self,error)
        }
        dbRequest.onsuccess=function(e){
            if(!self.connect)self.connect = e.target.result
            self.listener(INDEXDB_STATUES.CONNECT.SUCCESS)
            self.connect.onerror = function(e) {
                var error = e.target.error
                self.listener(INDEXDB_STATUES.CONNECT.SUCCESS, INDEXDB_STATUES.DATABASE.ERROR)
                options.error.call(self,error)
            };
            self.connect.onabort = function(e) {
                self.listener(INDEXDB_STATUES.CONNECT.SUCCESS, INDEXDB_STATUES.DATABASE.ABORT)
                options.abort.call(self, e.target.error)
            };
            options.success.call(self, self.connect)
            options.callback.call(self, self.connect)
        }
        dbRequest.onclose=function(e){
            self.listener(INDEXDB_STATUES.CONNECT.CLOSE)
            options.close.call(self, e.target.result)
            options.callback.call(self, e.target.result)
        }
        dbRequest.onupgradeneeded=function(e){
            self.listener(INDEXDB_STATUES.CONNECT.SUCCESS)
            if(!self.connect)self.connect = e.target.result
            for(var j=0;j<options.tables.length;j++){
                self.createTable(options.tables[j])
            }
            options.change.call(self, self.connect)
            options.callback.call(self, self.connect)
            console.debug('DB version changed to '+self.connect.version)
        }
    }

    /**
     * 重新打开链接
     * @param options
     */
    Database.prototype.reopen = function(options){
        if(!options || !options.version){
            options = options || {};
            var next = this.next();
            options.name = next.name;
            options.version = next.version;
        }
        this.close();
        this.open(options);
    }
    /**
     * 关闭连接
     */
    Database.prototype.close = function(){
        if(this.connect){
            this.connect.close()
            this.connect = null
        }
    }

    /**
     * 是否打开链接
     * @returns {boolean}
     */
    Database.prototype.connected = function(){
        return this.connect != null
    }

    /**
     * @param options {tables, mode, complete, error} tables
     * @param tables 需要启用事务的表
     * @param mode 事务类型  read write readwrite default
     */
    Database.prototype.transaction = function(options){
        var self = this;
        var defaultOptions = {
            tables : [],
            mode:TransactionMode.READONLY,
            complete:function(){},
            error:function(){},
            callback:function(){}
        };
        if(utils.isString(options) || utils.isArray(options)){
            options = {tables: options}
        }
        if(arguments.length>1){
            options.mode = arguments[1]
        }
        options = utils.extend(defaultOptions, options)
        var transaction = this.connect.transaction(options.tables, options.mode)
        transaction.oncomplete = function(e) {
            options.complete.call(self, e)
        };
        transaction.onerror = function(e) {
            var error = e.target.error
            console.error(e.target.source.name + " " + error.message)
            options.error.call(self, error)
        };
        options.callback.call(self)
        return transaction
    }

    /**
     * 下一个版本
     * @returns {null|Object|*}
     */
    Database.prototype.next = function(){
        return {
            name:this.connect ? this.connect.name : "default",
            version:this.connect ? this.connect.version + 1 : 1
        }
    }

    /**
     * 创建表
     * table : {"name":"student", "key":"id","autoIncrement":true, "indexes":[{"name":"aindex", "column":"name", "unique":true}]},
     * @param table 表对象 或者表名称 或者表对象数组
     * @param isChange 是否改变版本
     * @param success 当且仅当isChange==true时有用
     * @returns {*}
     */
    Database.prototype.createTable = function(table, isChange, success){
        table = utils.isString(table) ? {name:table}:table;
        if(isChange){
            this.reopen({
                tables:utils.isArray(table)?table:[table],
                success:success||function(){}
            });
            return;
        }
        var tables = utils.isArray(table) ? table : [table];

        for(var j=0;j<tables.length;j++){
            var table = tables[j];
            if(this.hasTable(table.name)) continue;
            var objectStore = this.connect.createObjectStore(table.name, {keyPath: table["key"]||"id", autoIncrement: !!table["autoIncrement"]});
            if(!utils.isUndefined(table["indexes"])){
                for(var i=0; i<table["indexes"].length; i++){
                    var index = table["indexes"][i];
                    objectStore.createIndex(index["name"],index["column"],{unique:!!index["unique"], multiEntry:!!index["multiEntry"]}); //创建索引
                    if(table.datas){
                        for(var k=0;k<table.datas.length;k++){
                            objectStore.add(table.datas[k]);
                        }
                    }
                }
            }
        }
    }

    /**
     * 删除表
     * @param name 表名
     */
    Database.prototype.dropTable = function(name){
        var self = this
        self.reopen({
            change:function(){
                self.connect.deleteObjectStore(name);
            }
        });
    };
    /**
     * 获取所有的表名
     * @returns {*}
     */
    Database.prototype.tables = function(){
        return this.connect["objectStoreNames"];
    }

    Database.prototype.table = function(name){
        return new Table(name, this);
    }

    /**
     * 是否包含某个表
     * @param tableName
     * @returns {*|boolean}
     */
    Database.prototype.hasTable = function(tableName){
        return this.tables()&&this.tables().contains(tableName);
    }

    /**
     * 删除索引
     * @param tableName 表名
     * @param indexName 索引名
     */
    Database.prototype.dropIndex = function(tableName, indexName){
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        objectStore.deleteIndex(indexName);
    }
    /**
     * 数据插入
     * @param tableName 表名
     * @param datas 数据内容
     */
    Database.prototype.insert = function(tableName, datas){
        var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
        var objectStore = transaction.objectStore(tableName);
        datas = utils.isArray(datas) ? datas : [datas];
        for(var i=0; i<datas.length; i++){
            objectStore.add(datas[i]);
        }
    }
    Database.prototype.update = function(tableName, newVal){
        var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
        var objectStore = transaction.objectStore(tableName);
        newVal = utils.isArray(newVal) ? newVal : [newVal];
        for(var i=0; i<newVal.length; i++){
            objectStore.put(newVal[i]);
        }
    }

    /**
     * 第一个开始
     */
    Database.prototype.pop = function(tableName, callback, target){
        var self = this;
        var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
        var objectStore = transaction.objectStore(tableName);
        var request = objectStore.openCursor(null, CursorDirection.NEXT);
        request.onsuccess = function(e){
            var cursor = e.target.result;
            if(cursor) {
                if(callback)callback.call(target||self, cursor.key, cursor.value);
                cursor["continue"]();
                objectStore["delete"](cursor.key);
            }
        };
        request.onerror = function(e){
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }
    /**
     * 末尾开始取值
     */
    Database.prototype.shift = function(tableName, callback, target){
        var self = this;
        var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
        var objectStore = transaction.objectStore(tableName);
        var request = objectStore.openCursor(CursorDirection.PREV);
        request.onsuccess = function(e){
            var cursor = e.target.result;
            if(cursor) {
                if(callback)callback.call(target||self, cursor.key, cursor.value);
                cursor["continue"]();
                objectStore["delete"](cursor.key);
            }
        }
        request.onerror = function(e){
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }

    /**
     * @param tableName
     * @param callback
     * @param query A value that is or resolves to an KeyRange.
     * @param count Specifies the number of values to return if more than one is found.
     */
    Database.prototype.query = function(tableName, callback, query, count, target){
        var self = this;
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        var request = objectStore.getAll(query, count);
        request.onsuccess = function(e){
            if(callback)callback.call(target||self, e.target.result);
        }
        request.onerror = function(e){
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }
    /**
     * 根据主键查找
     * @param tableName
     * @param key 主键
     * @param callback
     */
    Database.prototype.queryByKey = function(tableName, key, callback, target){
        var self = this;
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        var request = objectStore.get(key);
        request.onsuccess = function(e){
            if(callback)callback.call(target||self, e.target.result);
        }
        request.onerror = function(e){
            if(callback)callback.call(target||self, e.target.result);
        }
    }

    Database.prototype.queryAll = function(tableName, callback, target){
        var self = this;
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        var request = objectStore.getAll();
        request.onsuccess = function(e){
            if(callback)callback.call(target||self, e.target.result);
        }
        request.onerror = function(e){
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }

    /**
     * 使用游标循环
     * @param tableName
     * @param callback
     */
    Database.prototype.fetch = function(tableName, callback, keyRange, cursorDirection, target){
        var self = this;
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        var request = keyRange?objectStore.openCursor():objectStore.openCursor(keyRange, cursorDirection || CursorDirection.NEXT);
        request.onsuccess = function(e){
            var cursor = e.target.result;
            if(cursor) {
                if(callback)callback.call(target||self, cursor.key, cursor.value);
                cursor["continue"]();
            }
        };
        request.onerror = function(e){
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }
    /**
     * 使用索引游标查找
     * @param tableName
     * @param indexName
     * @param key allKeys > getAllKeys()
     * @param callback
     */
    Database.prototype.index = function(tableName, indexName, callback, key, target){
        var self = this;
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        var index = objectStore.index(indexName);
        var request = key == KeyRange.ALL_KEYS ? index.getAllKeys() : key ? index.get(key) : index.getAll();
        request.onsuccess=function(e){
            if(callback)callback.call(target||self, e.target.result);
        }
        request.onerror = function(e){
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }
    Database.prototype.multiple = function(tableName, indexName, callback, keyRange, cursorDirection, target){
        var self = this;
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        var index = objectStore.index(indexName);
        var cursor = index.openCursor(keyRange, cursorDirection || CursorDirection.NEXT);
        cursor.onsuccess = function(e){
            var cursor= e.target.result;
            if(cursor){
                callback.call(target||self, cursor.key ,cursor.value);
                cursor["continue"]();
            }
        }
        cursor.onerror = function(e){
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }

    Database.prototype.getKeys = function(tableName, callback, query, count, target){
        var self = this;
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        var request = query ? objectStore.getAllKeys(query, count) : objectStore.getAllKeys();
        request.onsuccess = function(e){
            if(callback)callback.call(target||self, e.target.result);
        }
        request.onerror = function(e){
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }

    /**
     * @param tableName
     * @param query A value that is or resolves to an KeyRange.
     * @param count Specifies the number of values to return if more than one is found.
     */
    Database.prototype.getAllKeys = function(tableName, callback, target){
        this.getKeys(tableName, callback, null, null, target);
    }

    Database.prototype.deleteRow = function(tableName, key){
        var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
        var objectStore = transaction.objectStore(tableName);
        objectStore["delete"](key);
    }
    Database.prototype.clear = function(tableName){
        var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
        var objectStore = transaction.objectStore(tableName);
        objectStore.clear();
    }

    /**
     * 统计
     * @param tableName 表名
     * @param keyRange 范围或者key值
     *  eg: count("asd") or count(KeyRange.eq("sdf")) or count()
     *  @see KeyRange
     * @param callback 回调
     */
    Database.prototype.count = function(tableName, callback, keyRange, target){
        var self = this;
        var transaction = this.transaction(tableName);
        var objectStore = transaction.objectStore(tableName);
        var countRequest = keyRange ? objectStore.count(keyRange) : objectStore.count();
        countRequest.onsuccess = function(e) {
            if(callback)callback.call(target||self, e.target.result);
        }
        countRequest.onerror = function(e) {
            console.error(e.target.error);
            if(callback)callback.call(target||self, e.target.result);
        }
    }

    Database.prototype.drop = function () {
        innerDB.deleteDatabase(this.name)
    }

    /*****************************************Database********************************************/
    /*****************************************Table********************************************/
    /**
     * 数据表对象
     * @param name 表名称
     * @param database 对应的数据库
     * @constructor
     */
    function Table(name, database){
        this.name = name;
        this.database = database;
    }

    Table.prototype.pop = function(callback){
        this.database.pop(this.name, callback, this);
    }
    Table.prototype.shift = function(callback){
        this.database.shift(this.name, callback, this);
    }
    Table.prototype.query = function(callback, query, count){
        this.database.query(this.name, callback, query, count, this)
    }
    Table.prototype.queryByKey = function(key, callback){
        this.database.queryByKey(this.name, key, callback, this);
    }
    Table.prototype.queryAll = function(callback){
        this.database.queryAll(this.name, callback, this);
    }
    Table.prototype.insert = function(datas){
        this.database.insert(this.name, datas);
    }
    Table.prototype.update = function(newVal){
        this.database.update(this.name, newVal);
    }
    Table.prototype.deleteRow = function(key){
        this.database.deleteRow(this.name, key);
    }
    Table.prototype.clear = function(){
        this.database.clear(this.name);
    }
    Table.prototype.fetch = function(callback, keyRange, cursorDirection){
        this.database.fetch(this.name, callback, keyRange, cursorDirection, this);
    }
    Table.prototype.index = function(indexName, callback, key){
        this.database.index(this.name, indexName, callback, key, this);
    }
    Table.prototype.multiple = function(indexName, callback, keyRange, cursorDirection){
        this.database.multiple(this.name, indexName, callback, keyRange, cursorDirection, this);
    }
    Table.prototype.getKeys = function(callback, query, count){
        this.database.getKeys(this.name, callback, query, count, this);
    }
    Table.prototype.getAllKeys = function(callback){
        this.database.getAllKeys(this.name, callback, this);
    }
    Table.prototype.count = function(callback, keyRange){
        this.database.count(this.name, callback, keyRange, this);
    }
    /*****************************************Table********************************************/

    function Driver(){
        this.sysdb = new Database("sys_db")
        this.pool = {}
        this.cdb = new Database()
        this.version = "2.0"
        this._init()
    }

    Driver.prototype._init = function () {
        var self = this
        self.sysdb.open({
            tables:[
                //系统参数
                {"name":"sys_params", "key":"key"},
                {"name":"sys_db_record", "key":"database"},
                {"name":"sys_logs", "key":"id", "autoIncrement":true, "indexes":[{"name":"logIndex", "column":"timestamp", "unique":true}]}
            ],
            success:function(){
                self.sysdb.queryAll("sys_db_record", function(all){
                    all && all.forEach(function (record) {
                        var name = record.database
                        var version = record.version
                        if(!self.pool[name] || self.pool[name].version < version){
                            self.pool[name] = new Database(name, version)
                        }
                    })
                    self.sysdb.queryByKey("sys_params", "using_db", function (database) {
                        if(database && self.pool[database.value]){
                            self.cdb.open(database.value, database.version)
                        }
                    })
                })
            }
        })
    }

    Driver.prototype.open = function (options) {
        var self = this
        if (self.sysdb.connected()) {
            self.cdb.open(options)
        } else {
            self.sysdb.addListener(function (state) {
                state === window.INDEXDB_STATUES.CONNECT.SUCCESS && self.cdb.open(options)
            })
        }
        record.call(this)
    }

    Driver.prototype.close = function () {
        var self = this
        self.sysdb.close()
        Object.keys(self.pool).forEach(function (name, database) {
            database && database.close()
        })
    }

    Driver.prototype.select = function (database) {
        var self = this
        var get = self.pool[database]
        if(!get) {
            self.pool[database] = new Database(database)
        } else if(!get.connected()){
            get.open()
        }
        self.cdb = get
        record.call(self)
    }

    function inspect(fn) {
        var self = this
        if(self.connected) {
            fn.call(self)
        } else {
            self.cdb.addListener(function (state) {
                state === window.INDEXDB_STATUES.CONNECT.SUCCESS && fn.call(self)
            })
        }
    }

    /**
     *
     * @param name 表名
     * @param fn 回调函数 this指向当前表对象 使用表对象进行表数据相关操作
     */
    Driver.prototype.getTable = function (name, fn) {
        inspect.call(this, function () {
            fn.call(this.cdb.table(name), this.cdb.table(name))
        })
    }

    var record = function () {
        this.sysdb.update("sys_params", {"key":"using_db", "value":this.cdb.name, "version":this.cdb.version+1})
    }

    /**
     * 创建表
     * table : {"name":"student", "key":"id","autoIncrement":true, "indexes":[{"name":"aindex", "column":"name", "unique":true}]},
     * @param table 表对象 或者表名称 或者表对象数组
     * @param isChange 是否改变版本
     * @param success 当且仅当isChange:=true时有用
     * @returns {*}
     */
    Driver.prototype.createTable = function (table, success) {
        inspect.call(this, function () {
            this.cdb.createTable(table, true, success)
            record.call(this)
        })
    }
    /**
     * 删除表
     * @param name 表名
     */
    Driver.prototype.dropTable = function (name) {
        inspect.call(this, function () {
            record.call(this)
            this.cdb.dropTable(name)
        })
    }

    Driver.prototype.addListener = function (listener) {
        this.cdb.addListener(listener)
    }
    /**
     * 获取所有的表名
     * @returns {*}
     */
    Driver.prototype.tables = function () {
        return this.cdb.tables()
    }
    /**
     * 删除数据库
     * @param database 数据库名称
     */
    Driver.prototype.dropDatabase = function (name) {
        this.pool[name] ? this.pool[name].drop() : innerDB.deleteDatabase(name)
    }
    /**
     * 是否包含某个表
     * @param tableName
     * @returns {*|boolean}
     */
    Driver.prototype.hasTable = function (name) {
        return this.cdb.hasTable(name)
    }
    /**
     * 删除索引
     * @param tableName 表名
     * @param indexName 索引名
     */
    Driver.prototype.dropIndex = function (tableName, indexName) {
        return this.cdb.dropIndex(tableName, indexName)
    }
    Object.defineProperty(Driver.prototype, 'connected', {
        get: function () {
            return this.cdb.connected()
        }
    })
    var driver = new Driver()
    window.addEventListener("unload", function(){
        driver.close()
    })
    return driver
})